refactor: FailedBookingsByField to use Insights Routing Service#23259
refactor: FailedBookingsByField to use Insights Routing Service#23259
Conversation
- Move getFailedBookingsByRoutingFormGroup logic to InsightsRoutingBaseService - Replace legacy getWhereForTeamOrAllTeams with getBaseConditions() - Add failedBookingsByFieldInputSchema with date filtering support - Update tRPC endpoint to use createInsightsRoutingService pattern - Update component to pass startDate/endDate parameters - Remove legacy method from routing-events.ts Follows same pattern as PR #23031 for RoutedToPerPeriod refactoring Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
WalkthroughUI: FailedBookingsByField.tsx now imports and uses useInsightsRoutingParameters(), passing the returned object directly into trpc.viewer.insights.failedBookingsByField.useQuery. Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
- Add missing import for useInsightsRoutingParameters - Update component to follow same pattern as PR #23031 - Add columnFilters support to failedBookingsByFieldInputSchema - Maintain routingFormId parameter from useInsightsParameters Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
- Remove routingFormId parameter from getFailedBookingsByFieldData method - Update component to only use insightsRoutingParameters - Remove routingFormId from failedBookingsByFieldInputSchema - Let getBaseConditions handle all filtering including routing form filtering - Simplify tRPC endpoint to not pass separate routingFormId Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
…okingsByFieldData - Replace complex CTE approach with direct denormalized table query - Join with App_RoutingForms_Form to access field definitions from JSON - Use RoutingFormResponseField for response values - Filter failed bookings with bookingUid IS NULL - Maintain same data structure for UI compatibility - Follow existing service patterns for authorization and date filtering Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
packages/features/insights/server/trpc-router.ts (2)
890-895: Preserve error context or add logging for debuggability.Catching and rethrowing INTERNAL_SERVER_ERROR loses the root cause. Consider logging the original error (with safe metadata) or attaching a minimal message for observability.
Apply this small diff:
- try { - return await insightsRoutingService.getFailedBookingsByFieldData(); - } catch (e) { - throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); - } + try { + return await insightsRoutingService.getFailedBookingsByFieldData(); + } catch (e) { + ctx.logger?.error?.({ err: e, route: "viewer.insights.failedBookingsByField" }, "failedBookingsByField error"); + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); + }
888-896: (Optional) Guardrail: enforce presence of routing form filter at the edge.UI currently guarantees a routingFormId, but adding a lightweight guard prevents accidental regressions that could reintroduce the “all forms” bug.
Proposed diff:
.query(async ({ ctx, input }) => { const insightsRoutingService = createInsightsRoutingService(ctx, input); + const hasFormIdFilter = + (input.columnFilters || []).some((f) => f.id === "formId" && !!(f as any).value?.data); + if (!hasFormIdFilter) { + // Defensive: UI should always pass a formId, keep server strict. + throw new TRPCError({ code: "BAD_REQUEST", message: "routingFormId is required" }); + } try { return await insightsRoutingService.getFailedBookingsByFieldData(); } catch (e) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); } })packages/features/insights/components/routing/FailedBookingsByField.tsx (1)
134-136: Pass-through of insightsRoutingParams to TRPC matches the new endpoint; consider enabling only when formId exists.The query call is correct. As a minor guard, you could enable the query only when the hook has emitted a formId to avoid any early, empty fetch cycles.
Optional diff:
- const { data } = trpc.viewer.insights.failedBookingsByField.useQuery(insightsRoutingParams); + const enabled = (insightsRoutingParams.columnFilters || []).some((f) => f.id === "formId"); + const { data } = trpc.viewer.insights.failedBookingsByField.useQuery(insightsRoutingParams, { enabled });packages/lib/server/service/InsightsRoutingBaseService.ts (3)
436-437: Qualifying filters with rfrd. is correct; minor robustness tweaks possible.*
- The aliasing fixes are spot-on and ensure filters hit the denormalized source consistently.
- For bookingUserId ANY, consider coercing to numeric to avoid accidental text[] bindings from UI.
Proposed diff:
- conditions.push(Prisma.sql`rfrd."bookingUserId" = ANY(${memberUserIds.value.data})`); + conditions.push(Prisma.sql`rfrd."bookingUserId" = ANY(${memberUserIds.value.data.map(Number)})`);If bookingUserId filter values can be strings, this prevents a Postgres type mismatch. If they’re guaranteed numbers upstream, feel free to skip.
Also applies to: 453-454, 462-463, 471-472, 505-506, 513-514
760-796: Sort options within each field for clearer UX; ORDER BY in SQL is redundant post-aggregation.
- After grouping, options per field aren’t sorted; sort descending by count to show most frequent failures first.
- Since you re-aggregate in TS, the SQL ORDER BY count DESC is not required; removing it can save the planner unnecessary work.
Apply this diff:
- const groupedByFormAndField = result.reduce((acc, curr) => { + const groupedByFormAndField = result.reduce((acc, curr) => { const formKey = curr.formName; acc[formKey] = acc[formKey] || {}; const labelKey = curr.fieldLabel; acc[formKey][labelKey] = acc[formKey][labelKey] || []; acc[formKey][labelKey].push({ optionId: curr.optionId, count: curr.count, optionLabel: curr.optionLabel, }); return acc; }, {} as Record<string, Record<string, { optionId: string; count: number; optionLabel: string }[]>>); + + // Sort options within each field by count desc for better readability + for (const fields of Object.values(groupedByFormAndField)) { + for (const label of Object.keys(fields)) { + fields[label] = fields[label].sort((a, b) => b.count - a.count); + } + }Optionally remove the SQL ORDER BY:
- WHERE ff.option_id IS NOT NULL - ORDER BY count DESC + WHERE ff.option_id IS NOT NULL
697-796: Keying by formName risks collisions if multiple forms share the same name (when formId filter is absent).UI currently enforces a routingFormId, so you’re safe. If that ever changes, consider keying by formId and passing formName alongside to avoid conflation.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
packages/features/insights/components/routing/FailedBookingsByField.tsx(2 hunks)packages/features/insights/server/routing-events.ts(0 hunks)packages/features/insights/server/trpc-router.ts(1 hunks)packages/lib/server/service/InsightsRoutingBaseService.ts(9 hunks)
💤 Files with no reviewable changes (1)
- packages/features/insights/server/routing-events.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/features/insights/server/trpc-router.tspackages/lib/server/service/InsightsRoutingBaseService.tspackages/features/insights/components/routing/FailedBookingsByField.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.539Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.
📚 Learning: 2025-07-15T12:59:34.389Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
Applied to files:
packages/features/insights/server/trpc-router.tspackages/lib/server/service/InsightsRoutingBaseService.tspackages/features/insights/components/routing/FailedBookingsByField.tsx
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.
Applied to files:
packages/features/insights/server/trpc-router.ts
📚 Learning: 2025-08-14T10:48:52.586Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/ai/_router.ts:46-84
Timestamp: 2025-08-14T10:48:52.586Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/ai/_router.ts, the voiceId input parameter in the create endpoint is intentionally not forwarded to aiService.createAgent() as voice customization is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.
Applied to files:
packages/features/insights/server/trpc-router.ts
📚 Learning: 2025-07-24T08:39:06.185Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22702
File: packages/lib/server/service/insightsBooking.ts:120-124
Timestamp: 2025-07-24T08:39:06.185Z
Learning: In the InsightsBookingService (packages/lib/server/service/insightsBooking.ts), the constructor stores null for invalid options or filters but this is handled safely through null checks in buildFilterConditions() and buildAuthorizationConditions() methods. The service uses defensive programming to return safe fallback conditions (null or NOTHING_CONDITION) rather than throwing errors on invalid inputs.
Applied to files:
packages/lib/server/service/InsightsRoutingBaseService.ts
📚 Learning: 2025-07-15T13:02:17.403Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/lib/server/service/insightsRouting.ts:367-368
Timestamp: 2025-07-15T13:02:17.403Z
Learning: In the InsightsRoutingService (packages/lib/server/service/insightsRouting.ts), multi-select filter data is already validated by zod before reaching the buildFormFieldSqlCondition method, so null/undefined values are not present in filterValue.data arrays and don't need to be filtered out.
Applied to files:
packages/lib/server/service/InsightsRoutingBaseService.ts
📚 Learning: 2025-07-15T12:58:40.539Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.539Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.
Applied to files:
packages/lib/server/service/InsightsRoutingBaseService.tspackages/features/insights/components/routing/FailedBookingsByField.tsx
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.
Applied to files:
packages/lib/server/service/InsightsRoutingBaseService.ts
🔇 Additional comments (4)
packages/features/insights/server/trpc-router.ts (1)
888-896: Endpoint refactor to InsightsRoutingService looks correct; input contract aligned.
- Switching to insightsRoutingServiceInputSchema and delegating to createInsightsRoutingService(ctx, input).getFailedBookingsByFieldData() matches the new service API and fixes the legacy coupling. Looks good.
packages/features/insights/components/routing/FailedBookingsByField.tsx (1)
19-19: Hook swap to useInsightsRoutingParameters is aligned with the new backend input shape.The import change is correct and consistent with the TRPC schema refactor.
packages/lib/server/service/InsightsRoutingBaseService.ts (2)
566-566: Authorization parity with rfrd aliasing looks right; validate scope semantics against legacy behavior.
- user: rfrd.formUserId = userId AND rfrd.formTeamId IS NULL
- org: teamIds OR user’s personal forms
- team: exact teamId
This matches expectations for routing responses. Please double-check any edge cases where orgId may be null under “user” scope so validation doesn’t null out options.
Would you like a small unit-test harness for getAuthorizationConditions across scopes?
Also applies to: 587-587, 603-604
697-759: CTE accurately computes failed bookings by form/field/option; zero-count options preserved.
- Good: baseConditions applied; failed defined as bookingUid IS NULL; DISTINCT response counting prevents duplicates; options expanded from form JSON and left-joined to include zero-counts.
- This directly addresses “selected form only” when formId is present in columnFilters.
Graphite Automations"Add consumer team as reviewer" took an action on this PR • (08/22/25)1 reviewer was added to this PR based on Keith Williams's automation. |
E2E results are ready! |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts (1)
593-600: Order-dependent assertion without ORDER BY can flake.SELECT id ... WHERE ${baseConditions} has no ORDER BY, yet the test asserts fixed positions (results[0], results[1]). This can intermittently fail.
Apply this diff to make the assertion order-agnostic (mirroring your later test):
- // Should only return the authorized user's form response - expect(results).toHaveLength(2); - expect(results[0]?.id).toBe(testData.formResponse.id); - expect(results[1]?.id).toBe(personalFormResponse.id); + // Should return both the user's team form response and the personal form response (order-agnostic) + expect(results).toHaveLength(2); + const responseIds = results.map((r) => r.id).sort(); + const expectedIds = [testData.formResponse.id, personalFormResponse.id].sort(); + expect(responseIds).toEqual(expectedIds);Also update the preceding comment to reflect that two rows are expected, not “only” one.
🧹 Nitpick comments (6)
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts (6)
912-915: Enum source-of-truth for bookingStatusOrder and bookingUserId filters.The tests use string literals (e.g., "pending", "accepted"). If bookingStatusOrder maps to an enum, prefer referencing the enum constants in test construction to catch drift early. For user IDs, ensure the ANY array is typed (see earlier comment).
If bookingStatusOrder is an enum, consider:
- Using BookingStatus.PENDING/ACCEPTED (if values match), or
- A mapping helper to translate enum to stored string in the denormalized view.
Also applies to: 1029-1032
1067-1076: Subqueries for attendee filters: correctness good; ensure indexes for performance.The EXISTS subqueries joining Booking and Attendee look correct and safe (parameterized ILIKE). For production scale, confirm indexes exist on:
- Attendee.bookingId
- Attendee.name, Attendee.email, Attendee.phoneNumber (consider trigram or lower(text) indexes if these predicates are hot)
Would you like me to propose index DDLs based on current query patterns?
Also applies to: 1111-1120, 1155-1164
1247-1255: Multi-select semantics: confirm “match all” (@>) vs “overlap any” (&&).Using @> means the response must contain all selected options. If the UI’s multi-select filter is intended as “any of” semantics, use the overlap operator (&&) instead.
If “any-of” is desired, adapt the expectation to:
-AND rrf."valueStringArray" @> ${["option1", "option2"]} +AND rrf."valueStringArray" && ${Prisma.sql`ARRAY[${Prisma.join(["option1","option2"])}]`}::text[]And mirror the same in the service implementation.
1306-1316: Brittle parentheses in combined condition expectations.The nested parentheses make tests sensitive to harmless formatting changes in the builder. Consider composing with intermediate fragments and a normalizer or assert via substring presence of each conjunct.
Example approach inside the test:
- Build expected parts individually (date, status, reason, custom field).
- Combine via Prisma.sql
${part1} AND ${part2} AND ${part3}to reduce nesting depth.
90-104: Make field IDs consistent between form definition and response payload.You generate random IDs for fields, then use different random IDs as keys in the response. This works for current tests (as they don’t assert field-level joins here) but can break future tests or denormalization assumptions. Reuse the same IDs for both.
Apply this refactor:
@@ - const form = await prisma.app_RoutingForms_Form.create({ + const textFieldId = `text-field-id-${randomUUID()}`; + const emailFieldId = `email-field-id-${randomUUID()}`; + const form = await prisma.app_RoutingForms_Form.create({ data: { name: "Test Routing Form", userId: user.id, teamId: team.id, fields: [ { - id: `text-field-id-${randomUUID()}`, + id: textFieldId, type: "text", label: "Name", required: true, }, { - id: `email-field-id-${randomUUID()}`, + id: emailFieldId, type: "email", label: "Email", required: true, }, ], }, }); @@ - const formResponse = await prisma.app_RoutingForms_FormResponse.create({ + const formResponse = await prisma.app_RoutingForms_FormResponse.create({ data: { formFillerId: `test-filler-${randomUUID()}`, formId: form.id, response: { - [`text-field-id-${randomUUID()}`]: { + [textFieldId]: { label: "Name", value: "Test Name", }, - [`email-field-id-${randomUUID()}`]: { + [emailFieldId]: { label: "Email", value: "test@example.com", }, }, routedToBookingUid: booking.uid, }, });Also applies to: 113-121
249-257: Broaden coverage: add tests for getFailedBookingsByFieldData() output shape.This file thoroughly tests auth/filter fragments. Given this PR introduces getFailedBookingsByFieldData(), consider adding at least one integration test asserting the nested form → field → option counts shape and date filtering respects rfrd aliasing.
I can draft a minimal test that:
- Creates one form with two fields and two responses, one failed (no routedToBookingUid), one successful.
- Calls service.getFailedBookingsByFieldData() with routingFormId filter and date bounds.
- Asserts aggregation counts and that only the selected form is included.
Would you like me to open a follow-up PR with this test?Also applies to: 516-544
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts(25 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/RoutingFunnel.tsx:15-17
Timestamp: 2025-07-15T12:58:40.539Z
Learning: In the insights routing funnel component (packages/features/insights/components/RoutingFunnel.tsx), the useColumnFilters exclusions are intentionally different from the general useInsightsParameters exclusions. RoutingFunnel specifically excludes only ["createdAt"] while useInsightsParameters excludes ["bookingUserId", "formId", "createdAt", "eventTypeId"]. This difference is by design.
📚 Learning: 2025-08-22T16:38:00.182Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23192
File: packages/lib/server/service/InsightsBookingBaseService.ts:814-816
Timestamp: 2025-08-22T16:38:00.182Z
Learning: In the InsightsBookingBaseService (packages/lib/server/service/InsightsBookingBaseService.ts), when filtering for "accepted" bookings in getMembersStatsWithCount(), using `endTime <= now()` in the SQL condition should be avoided as it conflicts with existing date filtering logic. The components already handle completion filtering by setting `endDate: currentTime` in their query parameters, making additional SQL-level endTime filtering unnecessary and potentially problematic.
Applied to files:
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts
🔇 Additional comments (7)
packages/lib/server/service/__tests__/InsightsRoutingService.integration-test.ts (7)
326-329: Good aliasing and user-scope condition assertion.Using the rfrd alias consistently and asserting the exact user-scope condition looks correct and aligns with the service refactor.
351-351: Team-scope condition LGTM.The equality on rfrd."formTeamId" is precise and matches the intended authorization boundary for team scope.
478-481: Caching assertion is solid.Confirming identical Sql fragments on repeat calls is a good proxy for cache correctness.
524-540: Readable composition of base conditions.Binding dateCondition separately and composing it into the final Sql keeps intent clear and reduces duplication. Good pattern.
1202-1210: Custom field TEXT filter LGTM.EXISTS on RoutingFormResponseField by fieldId with valueString equality is straightforward and matches expectations.
814-823: Time control in columnFilters suite is solid.Freezing time ensures deterministic date windows. Good practice here.
451-452: Incorrect suggestion regarding timestamp type
ThecreatedAtcolumn onRoutingFormResponseDenormalizedis defined in Prisma with@db.Timestamp(3), which maps to a SQLTIMESTAMP(3)without time zone. Casting the test literals to::timestamptherefore correctly matches the column’s type. Switching to::timestamptzwould introduce a type mismatch rather than fix one. The recommendation assumed atimestamptzcolumn, but the schema shows otherwise—no change is needed here.Likely an incorrect or invalid review comment.
What does this PR do?
Refactors the
FailedBookingsByFieldcomponent to use the centralizedInsightsRoutingServiceinstead of the legacy authorization method, following the same pattern established in PR #23031.It also fixes the bug that the previous implementation was not displaying the result for the selected routing form but for all the forms.
Visual Demo (For contributors especially)
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
Environment setup:
routedToBookingUid)Checklist
🔍 Review Focus Areas
High Priority:
routing-events.tstoInsightsRoutingBaseServicegetBaseConditions()provides same access control as the original manual team filteringscope/selectedTeamIdcorrectly replaceisAll/teamId/userIdparametersTesting Required:
Link to Devin run: https://app.devin.ai/sessions/9f69c66f0ff74b7a86ffbca3ab992480
Requested by: @eunjae-lee